
package com.ifcar.app.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.support.v4.view.MotionEventCompat;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.widget.ImageView;

import com.ifcar.app.util.ThemeSettingsHelper;
import com.ifcar.app.util.VersionCompat;
import com.ifcar.app.activity.R;

import java.lang.ref.WeakReference;

public class ShowBigView extends ImageView {

    @SuppressWarnings("unused")
    private static final String TAG = "ShowBigView";

    protected Matrix mBaseMatrix = new Matrix();

    protected Matrix mSuppMatrix = new Matrix();

    private final Matrix mDisplayMatrix = new Matrix();

    // Temporary buffer used for getting the values out of a matrix.
    private final float[] mMatrixValues = new float[9];

    protected WeakReference<Bitmap> image = null;

    int mThisWidth = -1, mThisHeight = -1;

    private boolean onclickClose = true;

    float mMaxZoom = 3.0f;// 最大缩放比例
    float mMinZoom;// 最小缩放比例

    private int imageWidth;// 图片的原始宽度
    private int imageHeight;// 图片的原始高度

    private float scaleRate;// 图片适应屏幕的缩放比例

    private int titleheight = 0; // 在查看原图时记录传过来的页面的标题的高度；

    private GestureDetector gestureDetector;

    private int screenWidth;
    private int screenHeight;

    private Runnable mOnLayoutRunnable;

    public ShowBigView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    public ShowBigView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ShowBigView(Context context) {
        super(context);
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        onDrawExtra(canvas);
    }

    protected void onDrawExtra(Canvas canvas) {
        if (ThemeSettingsHelper.getThemeSettingsHelper(getContext()).isNightTheme(getContext())) {
            canvas.drawARGB(125, 0, 0, 0);
        }
    }

    public void setWithHeigh(int imageWidth, int imageHeight) {
        this.imageHeight = imageHeight;
        this.imageWidth = imageWidth;
    }

    public void setScale(int width, int height)
    {
        screenWidth = width;
        screenHeight = height;
    }

    /**
     * 计算图片要适应屏幕需要缩放的比例
     */
    private void arithScaleRate() {
        float scaleWidth = screenWidth / (float) imageWidth;
        float scaleHeight = screenHeight
                / (float) imageHeight;
        scaleRate = Math.min(scaleWidth, scaleHeight);
    }

    public float getScaleRate() {
        return scaleRate;
    }

    public int getImageWidth() {
        return imageWidth;
    }

    public void setImageWidth(int imageWidth) {
        this.imageWidth = imageWidth;
    }

    public int getImageHeight() {
        return imageHeight;
    }

    public void setImageHeight(int imageHeight) {
        this.imageHeight = imageHeight;
    }

    //
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (getScale() > 1.0f) {
                zoomTo(1.0f);
                return true;
            }
        }
        return super.onKeyUp(keyCode, event);
    }

    protected Handler mHandler = new Handler();

    public void setImageBitmap() {
        Drawable d = getDrawable();
        Bitmap b = d instanceof BitmapDrawable ? ((BitmapDrawable) d).getBitmap()
                : null;
        if (b != null)
            setImageBitmap(b);
        else if(image!=null)
            setImageBitmap(image.get());
    }

    @Override
    public void setImageBitmap(final Bitmap bitmap) {
        if (bitmap != null)
            image = new WeakReference<Bitmap>(bitmap);

        if (getHeight() <= 0 || (getHeight() > 0 && isLayoutRequested() && !mForceSet)) {
            mOnLayoutRunnable = new Runnable() {
                public void run() {
                    setImageBitmap(bitmap);
                }
            };
            return;
        }

        if (bitmap != null)
        {
            this.imageWidth = bitmap.getWidth();
            this.imageHeight = bitmap.getHeight();
            arithScaleRate();
            Matrix matrix = new Matrix();
            matrix.postScale(getScaleRate(), getScaleRate());
            super.setImageBitmap(bitmap);
            // layoutToCenter();
            center(true, true);
        }
        else {
            super.setImageBitmap(null);
        }

    }

    protected void center(boolean horizontal, boolean vertical) {
        if (image == null) {
            return;
        }
        Matrix m = getImageViewMatrix();
        RectF rect = new RectF(0, 0, image.get().getWidth(), image.get().getHeight());
        m.mapRect(rect);
        float height = rect.height();
        float width = rect.width();
        float deltaX = 0, deltaY = 0;

        if (vertical) {
            int viewHeight = getHeight();
            if (height < viewHeight) {
                deltaY = (viewHeight - height) / 2 - rect.top;
            } else if (rect.top > 0) {
                deltaY = -rect.top;
            } else if (rect.bottom < viewHeight) {
                deltaY = getHeight() - rect.bottom;
            }
        }
        if (horizontal) {
            int viewWidth = getWidth();
            if (width < viewWidth) {
                deltaX = (viewWidth - width) / 2 - rect.left;
            } else if (rect.left > 0) {
                deltaX = -rect.left;
            } else if (rect.right < viewWidth) {
                deltaX = viewWidth - rect.right;
            }
        }
        postTranslate(deltaX, deltaY);
        setImageMatrix(getImageViewMatrix());
    }

    private void init() {
        setScaleType(ImageView.ScaleType.MATRIX);
        center(true, true);
        // 居中
        // layoutToCenter();

    }

    /**
     * 设置图片居中显示
     */
    public void layoutToCenter() {
        float fill_width = screenWidth - image.get().getWidth();
        float fill_height = screenHeight - image.get().getHeight();
        float tran_width = 0f;
        float tran_height = 0f;
        tran_width = fill_width / 2;
        tran_height = fill_height / 2;
        postTranslate(tran_width, tran_height - 50);
        setImageMatrix(getImageViewMatrix());
        this.invalidate();
    }

    public void reLayout() {

    }

    private boolean mForceSet;

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if (changed && mOnLayoutRunnable == null) {
            mOnLayoutRunnable = new Runnable() {
                public void run() {
                    setImageBitmap();
                }
            };
        }
        Runnable r = mOnLayoutRunnable;
        if (r != null) {
            mOnLayoutRunnable = null;
            mForceSet = true;
            r.run();
            mForceSet = false;
        }

        ViewParent parent = getParent();
        if (parent != null && parent instanceof View) {
            View titleView = ((View) parent).findViewById(R.id.top_relative);
            if (titleView != null) {
                titleheight = titleView.getMeasuredHeight();
            }
        }
    }

    protected float getValue(Matrix matrix, int whichValue) {
        matrix.getValues(mMatrixValues);
        mMinZoom = (screenWidth / 2f) / imageWidth;
        if (mMinZoom > 0.5f)
            mMinZoom = 0.5f;
        return mMatrixValues[whichValue];
    }

    protected float getScale(Matrix matrix) {
        return getValue(matrix, Matrix.MSCALE_X);
    }

    protected float getScale() {
        return getScale(mSuppMatrix);
    }

    protected Matrix getImageViewMatrix() {
        mDisplayMatrix.set(mBaseMatrix);
        mDisplayMatrix.postConcat(mSuppMatrix);
        return mDisplayMatrix;
    }

    static final float SCALE_RATE = 1.25F;

    protected float maxZoom() {
        if (image == null) {
            return 1F;
        }
        float fw = (float) image.get().getWidth() / (float) mThisWidth;
        float fh = (float) image.get().getHeight() / (float) mThisHeight;
        float max = Math.max(fw, fh) * 4;
        return max;
    }

    protected void zoomTo(float scale, float centerX, float centerY) {

        if (scale > mMaxZoom) {
            scale = mMaxZoom;
        } else if (scale < mMinZoom) {
            scale = mMinZoom;
        }

        float oldScale = getScale();
        float deltaScale = scale / oldScale;
        mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
        center(true, true);
    }

    protected void zoomTo(final float scale, final float centerX,
            final float centerY, final float durationMs) {
        final float incrementPerMs = (scale - getScale()) / durationMs;
        final float oldScale = getScale();
        final long startTime = System.currentTimeMillis();

        mHandler.post(new Runnable() {
            public void run() {
                long now = System.currentTimeMillis();
                float currentMs = Math.min(durationMs, now - startTime);
                float target = oldScale + (incrementPerMs * currentMs);
                zoomTo(target, centerX, centerY);
                if (currentMs < durationMs) {
                    mHandler.post(this);
                }
            }
        });
    }

    protected void zoomTo(float scale) {
        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        zoomTo(scale, cx, cy);
    }

    protected void zoomToPoint(float scale, float pointX, float pointY) {
        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        panBy(cx - pointX, cy - pointY);
        zoomTo(scale, cx, cy);
    }

    protected void zoomIn() {
        zoomIn(SCALE_RATE);
    }

    protected void zoomOut() {
        zoomOut(SCALE_RATE);
    }

    protected void zoomIn(float rate) {
        if (getScale() >= mMaxZoom) {
            return; // Don't let the user zoom into the molecular level.
        } else if (getScale() <= mMinZoom) {
            return;
        }
        if (image == null) {
            return;
        }

        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        mSuppMatrix.postScale(rate, rate, cx, cy);
        setImageMatrix(getImageViewMatrix());
    }

    protected void zoomOut(float rate) {
        if (image == null) {
            return;
        }

        float cx = getWidth() / 2F;
        float cy = getHeight() / 2F;

        // Zoom out to at most 1x.
        Matrix tmp = new Matrix(mSuppMatrix);
        tmp.postScale(1F / rate, 1F / rate, cx, cy);

        if (getScale(tmp) < 1F) {
            mSuppMatrix.setScale(1F, 1F, cx, cy);
        } else {
            mSuppMatrix.postScale(1F / rate, 1F / rate, cx, cy);
        }
        setImageMatrix(getImageViewMatrix());
        center(true, true);
    }

    public void postTranslate(float dx, float dy) {
        mSuppMatrix.postTranslate(dx, dy);
        setImageMatrix(getImageViewMatrix());
    }

    float _dy = 0.0f;

    protected void postTranslateDur(final float dy, final float durationMs) {
        _dy = 0.0f;
        final float incrementPerMs = dy / durationMs;
        final long startTime = System.currentTimeMillis();
        mHandler.post(new Runnable() {
            public void run() {
                long now = System.currentTimeMillis();
                float currentMs = Math.min(durationMs, now - startTime);

                postTranslate(0, incrementPerMs * currentMs - _dy);
                _dy = incrementPerMs * currentMs;

                if (currentMs < durationMs) {
                    mHandler.post(this);
                }
            }
        });
    }

    protected void panBy(float dx, float dy) {
        postTranslate(dx, dy);
        setImageMatrix(getImageViewMatrix());
    }

    float baseValue;
    float originalScale;
    float distanceX, distanceY;
    float prepointX = 0, postpointX, prepointY = 0, postpointY;
    private PointF midPoint;// 中间点
    private boolean move = false;

    private static final int PRE_ACTION_ZOOM = 1;
    private static final int PRE_ACTION_MOVE = 2;

    private int mode = -1;
    float oldDist = 1f;
    float scale;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction() & MotionEventCompat.getActionMasked(event)) {
            case MotionEvent.ACTION_DOWN:
                baseValue = 0;
                originalScale = getScale();
                // System.out.println(originalScale+" 343434");
                prepointX = event.getX();
                prepointY = event.getY();
                mode = PRE_ACTION_MOVE;
                move = false;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                mode = PRE_ACTION_ZOOM;
                midPoint = mid(event);
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == PRE_ACTION_MOVE)
                    dragMove(event);
                else if (mode == PRE_ACTION_ZOOM
                        && VersionCompat.getMotionEventCompat().getPointerCount(event) > 1)
                {
                    move = true;

                    float value = distance(event);
                    if (baseValue == 0) {
                        baseValue = value;
                    } else {
                        scale = value / baseValue;// 当前两点间的距离除以手指落下时两点间的距离就是需要缩放的比例。
                    }
                    if (Math.abs(value - baseValue) > 10.0f)
                        zoomTo(originalScale * scale, midPoint.x, midPoint.y);
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mode == PRE_ACTION_ZOOM)
                {
                    if (getScale() < 1.0f)
                    {
                        zoomTo(1.0f, midPoint.x, midPoint.y);
                    }
                    mode = -1;
                }
                if (!move)
                {
                    mode = -1;
                    if (this.onclickClose)
                    {
                        callback.onclick();
                    }
                }
                break;
        }
        return true;
    }

    public void dragMove(MotionEvent event)
    {
        postpointX = event.getX();
        postpointY = event.getY();

        distanceX = postpointX - prepointX;
        distanceY = postpointY - prepointY;
        prepointX = postpointX;
        prepointY = postpointY;

        float v[] = new float[9];
        Matrix m = getImageMatrix();
        m.getValues(v);
        // 图片实时的上下左右坐标
        float left, right, top, bottom;

        float width, height;
        width = getScale() * image.get().getWidth();
        height = getScale() * image.get().getHeight();

        left = v[Matrix.MTRANS_X];
        top = v[Matrix.MTRANS_Y];
        right = left + width;
        bottom = top + height;

        if (Math.abs(distanceX) > 5.0f || Math.abs(distanceY) > 5.0f)
            move = true;
        else
        {
            ;
            return;
        }

        if ((int) width <= screenWidth && (int) height <=
                screenHeight)
        {
            ;
        } else {
            if (right - left < (float) this.getWidth()) {
                distanceX = 0;
            } else if (left + distanceX > 0 && distanceX > 0)
                distanceX = -left;
            else if (right + distanceX < this.getRight() && distanceX < 0)
                distanceX = this.getRight() - right;
            if (bottom - top < this.getHeight()) {
                distanceY = 0;
            } else if (top + distanceY > 0 && distanceY > 0)
                distanceY = -top;
            else if (bottom + distanceY < (this.getBottom()) && distanceY < 0)
                distanceY = this.getBottom() - bottom - titleheight;

            postTranslate(distanceX, distanceY);
        }
    }

    /**
     * 计算两点之间的中间点‰
     * 
     * @param event
     * @return
     */
    public static PointF mid(MotionEvent event) {
        float x = MotionEventCompat.getX(event, 0) + MotionEventCompat.getX(event, 1);// event.getX(0)
                                                                                      // +
                                                                                      // event.getX(1);
        float y = MotionEventCompat.getY(event, 0) + MotionEventCompat.getY(event, 1);// event.getY(0)
                                                                                      // +
                                                                                      // event.getY(1);
        return new PointF(x / 2, y / 2);
    }

    /**
     * 计算两点之间的距离
     * 
     * @param event
     * @return
     */
    public static float distance(MotionEvent event) {
        float x = MotionEventCompat.getX(event, 0) - MotionEventCompat.getX(event, 1);
        float y = MotionEventCompat.getY(event, 0) - MotionEventCompat.getY(event, 1);
        return FloatMath.sqrt(x * x + y * y);
    }

    public void setOnClickClose(boolean close)
    {
        this.onclickClose = close;
    }

    public void setCallBack(onClickCallBack callback)
    {
        this.callback = callback;
    }

    private onClickCallBack callback;

    public interface onClickCallBack
    {
        public void onclick();
    }
}
